﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MainWindow.cs" company="ScRePt">
//   Copyright © ScRePt 2010 - 2012
// </copyright>
// <summary>
//   The main.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MP3D
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.IO;
    using System.Reflection;
    using System.Windows.Forms;

    using Calibration;
    using Configuration;
    using DirectShowLib;
    using Helper;

    using MediaPortal.Configuration;
    using MediaPortal.Dialogs;
    using MediaPortal.GUI.Library;
    using MediaPortal.Player;
    using MediaPortal.Player.Subtitles;
    using Stereoscopic;
    using Subtitles;
    using Zoom;

    using Menu = Configuration.Menu;

    #endregion

    /// <summary>
    /// The main.
    /// </summary>
    public class MainWindow : GUIWindow
    {
        #region Constants and Fields

        /// <summary>
        /// The tag current horizontal parallax.
        /// </summary>
        private const string TagCurrentHorizontalParallax = "#MP3D.CurrentHorizontalParallax";

        /// <summary>
        /// The tag current osd menu.
        /// </summary>
        private const string TagCurrentOsd = "#MP3D.CurrentOSD";

        /// <summary>
        /// The tag current output format.
        /// </summary>
        private const string TagCurrentOutputFormat = "#MP3D.CurrentOutputFormat";

        /// <summary>
        /// The tag current output format label.
        /// </summary>
        private const string TagCurrentOutputFormatLabel = "#MP3D.CurrentOutputFormatLabel";

        /// <summary>
        /// The tag current source format.
        /// </summary>
        private const string TagCurrentSourceFormat = "#MP3D.CurrentSourceFormat";

        /// <summary>
        /// The tag current source format label.
        /// </summary>
        private const string TagCurrentSourceFormatLabel = "#MP3D.CurrentSourceFormatLabel";

        /// <summary>
        /// The tag current vertical parallax.
        /// </summary>
        private const string TagCurrentVerticalParallax = "#MP3D.CurrentVerticalParallax";

        /// <summary>
        /// The tag cycle output formats label.
        /// </summary>
        private const string TagCycleOutputFormatsLabel = "#MP3D.CycleOutputFormatsLabel";

        /// <summary>
        /// The tag cycle source formats label.
        /// </summary>
        private const string TagCycleSourceFormatsLabel = "#MP3D.CycleSourceFormatsLabel";

        /// <summary>
        /// The tag horizontal parallax label.
        /// </summary>
        private const string TagHorizontalParallaxLabel = "#MP3D.HorizontalParallaxLabel";

        /// <summary>
        /// The tag Is3DMode.
        /// </summary>
        private const string TagIsMode3D = "#MP3D.IsMode3D";

        /// <summary>
        /// The tag mode 3 d title label.
        /// </summary>
        private const string TagMode3DTitleLabel = "#MP3D.Mode3DTitleLabel";

        /// <summary>
        /// The tag vertical parallax label.
        /// </summary>
        private const string TagVerticalParallaxLabel = "#MP3D.VerticalParallaxLabel";

        /// <summary>
        /// The calibrator.
        /// </summary>
        private Calibrator calibrator;

        /// <summary>
        /// The context menu shown.
        /// </summary>
        private bool contextMenuShown;

        /// <summary>
        /// The dlg.
        /// </summary>
        private GUIDialogMenu dlg;

        /// <summary>
        /// The hot key down.
        /// </summary>
        private Keys hotKeyDown;

        /// <summary>
        /// The hot key down modifiers.
        /// </summary>
        private Keys hotKeyDownModifiers;

        /// <summary>
        /// The hot key down tick.
        /// </summary>
        private DateTime hotKeyDownTick;

        /// <summary>
        /// The long press.
        /// </summary>
        private bool longPress;

        /// <summary>
        /// The monitor commands.
        /// </summary>
        private Collection<MenuCommand> monitorCommands;

        /// <summary>
        /// The monitor commands conditionally.
        /// </summary>
        private Collection<MenuCommand> monitorCommandsConditionally;

        /// <summary>
        /// The monitor commands from menu.
        /// </summary>
        private Collection<MenuCommand> monitorCommandsFromMenu;

        /// <summary>
        /// The output format property.
        /// </summary>
        private FilterPropertyEnum outputFormatProperty;

        /// <summary>
        /// The source format property.
        /// </summary>
        private FilterPropertyEnum sourceFormatProperty;

        /// <summary>
        /// The view mode switcher.
        /// </summary>
        private ViewModeSwitcher viewModeSwitcher;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindow"/> class.
        /// </summary>
        public MainWindow()
        {
            // Find out _currentMedia since g_Player has it private for some strange reason
            if (g_Player.IsVideo)
            {
                this.CurrentMediaType = g_Player.MediaType.Video;
            }
            else if (g_Player.IsTV)
            {
                this.CurrentMediaType = g_Player.MediaType.TV;
            }
            else if (g_Player.IsTVRecording)
            {
                this.CurrentMediaType = g_Player.MediaType.Recording;
            }
            else
            {
                this.CurrentMediaType = g_Player.MediaType.Unknown;
            }

            // Extract default skin if no skin was found
            if (!File.Exists(Helper.Helper.SkinFileOsd))
            {
                Helper.Helper.SaveResourceToFilename(Helper.Helper.SkinFileOsd, "MP3D.Skin.MP3D_OSD.xml");
            }

            if (!File.Exists(Path.Combine(Helper.Helper.SkinPathMedia, "Mode3d.png")))
            {
                Helper.Helper.SaveResourceToFilename(Path.Combine(Helper.Helper.SkinPathMedia, "Mode3d.png"), "MP3D.Skin.Media.MP3D.Mode3d.png");
            }

            if (!File.Exists(Path.Combine(Helper.Helper.SkinPathMedia, "Back_bottom.png")))
            {
                Helper.Helper.SaveResourceToFilename(Path.Combine(Helper.Helper.SkinPathMedia, "Back_bottom.png"), "MP3D.Skin.Media.MP3D.Back_bottom.png");
            }

            if (!File.Exists(Path.Combine(Helper.Helper.SkinPathMedia, "Back_top.png")))
            {
                Helper.Helper.SaveResourceToFilename(Path.Combine(Helper.Helper.SkinPathMedia, "Back_top.png"), "MP3D.Skin.Media.MP3D.Back_top.png");
            }

            if (!File.Exists(Path.Combine(Helper.Helper.SkinPathMedia, "option_enabled.png")))
            {
                Helper.Helper.SaveResourceToFilename(Path.Combine(Helper.Helper.SkinPathMedia, "option_enabled.png"), "MP3D.Skin.Media.MP3D.option_enabled.png");
            }

            if (!File.Exists(Path.Combine(Helper.Helper.SkinPathMedia, "option_disabled.png")))
            {
                Helper.Helper.SaveResourceToFilename(Path.Combine(Helper.Helper.SkinPathMedia, "option_disabled.png"), "MP3D.Skin.Media.MP3D.option_disabled.png");
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// The active window id.
        /// </summary>
        public int ActiveWindowId { get; set; }

        /// <summary>
        /// The current file hash.
        /// </summary>
        public string CurrentFileHash { get; set; }

        /// <summary>
        /// Gets or sets CurrentMediaType.
        /// </summary>
        public g_Player.MediaType CurrentMediaType { get; set; }

        /// <summary>
        /// Gets or sets CurrentOsd.
        /// </summary>
        public int CurrentOsd { get; set; }

        /// <summary>
        /// Gets or sets FilterWrapper.
        /// </summary>
        public StereoscopicFilter FilterWrapper { get; set; }

        /// <summary>
        /// Gets GetID.
        /// </summary>
        public override int GetID
        {
            get
            {
                return (int)Helper.Helper.PluginWindowIds.WindowIdOsd;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether IsMode3D.
        /// </summary>
        public bool IsMode3D { get; set; }

        /// <summary>
        /// Gets or sets Menu.
        /// </summary>
        public Menu Menu { get; set; }

        /// <summary>
        /// Gets or sets ParentPlugin.
        /// </summary>
        public Plugin ParentPlugin { get; set; }

        /// <summary>
        /// The settings.
        /// </summary>
        public Configuration.Configuration Settings { get; set; }

        /// <summary>
        /// Gets a value indicating whether SupportsDelayedLoad.
        /// </summary>
        public override bool SupportsDelayedLoad
        {
            get
            {
                return false;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The log error.
        /// </summary>
        /// <param name="methodName">
        /// The method name.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        public static void LogError(string methodName, string message)
        {
            string msg = string.Format("[MP3D]: MainWindow.cs: {0}: {1}", methodName, message);
            Log.Error(msg);
            Debug.Assert(true, msg);
        }

        /// <summary>
        /// The log warn.
        /// </summary>
        /// <param name="methodName">
        /// The method name.
        /// </param>
        /// <param name="formatMessage">
        /// The format message.
        /// </param>
        /// <param name="formatParameters">
        /// The format parameters.
        /// </param>
        public static void LogWarn(string methodName, string formatMessage, params object[] formatParameters)
        {
            LogWarn(methodName, string.Format(formatMessage, formatParameters));
        }

        /// <summary>
        /// The log warn.
        /// </summary>
        /// <param name="methodName">
        /// The method name.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        public static void LogWarn(string methodName, string message)
        {
            string msg = string.Format("[MP3D]: MainWindow.cs: {0}: {1}", methodName, message);
            Log.Warn(msg);
            Debug.Assert(true, msg);
        }

        /// <summary>
        /// The de init.
        /// </summary>
        public override void DeInit()
        {
            if (this.ActiveWindowId != 0)
            {
                foreach (GUIControl ctrl in this.controlList)
                {
                    if (ctrl != null)
                    {
                        GUIControl refCtrl = ctrl; // Stupid local ref compiler restriction
                        GUIWindowManager.GetWindow(this.ActiveWindowId).Remove(refCtrl.GetID);
                        ctrl.WindowId = this.GetID;
                        ctrl.ParentID = this.GetID;
                    }
                }
            }

            if (this.viewModeSwitcher != null)
            {
                this.viewModeSwitcher.Dispose();
                this.viewModeSwitcher = null;
            }

            if (this.calibrator != null)
            {
                this.calibrator.Dispose();
                this.calibrator = null;
            }

            base.DeInit();
        }

        /// <summary>
        /// The detect letterbox.
        /// </summary>
        public void DetectLetterbox()
        {
            if (this.Settings.DetectLetterbox && this.FilterWrapper.Initialized)
            {
                this.viewModeSwitcher = new ViewModeSwitcher();
                this.viewModeSwitcher.Crop += new ViewModeSwitcher.StereoscopicCropEventHandler(this.OnViewModeSwitcherCrop);
            }
            else if (this.viewModeSwitcher != null)
            {
                this.OnViewModeSwitcherCrop(0, 0, 0, 0);
                this.viewModeSwitcher.Dispose();
                this.viewModeSwitcher = null;
            }
        }

        /// <summary>
        /// The execute command.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The execute command.
        /// </returns>
        public bool ExecuteCommand(MenuCommand command)
        {
            int backwardSteps = 0;
            return this.ExecuteCommand(command, ref backwardSteps);
        }

        /// <summary>
        /// The init.
        /// </summary>
        /// <returns>
        /// The init.
        /// </returns>
        public override bool Init()
        {
            if (this.Load(Helper.Helper.SkinFileOsd))
            {
                this.IsMode3D = false;
                this.CurrentOsd = 21;
                this.Refresh(true);

                if (g_Player.Player != null)
                {
                    FieldInfo inf;
                    if (g_Player.Player is VideoPlayerVMR7)
                    {
                        VideoPlayerVMR7 p = (VideoPlayerVMR7)g_Player.Player;

                        // Nasty way of getting protected field graphBuilder ;-). God bless reflection!
                        inf = p.GetType().GetField("graphBuilder", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
                    }
                    else
                    {
                        // Nasty way of getting protected field graphBuilder ;-). God bless reflection!
                        inf = g_Player.Player.GetType().GetField("_graphBuilder", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
                    }

                    if (inf == null)
                    {
                        LogError("Init", "MP3D could not be initialized because graphBuilder could not be found!!");
                        return false;
                    }

                    object o = inf.GetValue(g_Player.Player);
                    IGraphBuilder graphBuilder = (IGraphBuilder)o;

                    this.FilterWrapper = new StereoscopicFilter(graphBuilder);
                    this.FilterWrapper.OutputFormatChanged += new StereoscopicFilter.FilterPropertyChangedEventHandler(this.OnOutputFormatChanged);
                    this.FilterWrapper.HorizontalParallaxChanged += new StereoscopicFilter.FilterPropertyChangedEventHandler(this.OnHorizontalParallaxChanged);
                    this.FilterWrapper.VerticalParallaxChanged += new StereoscopicFilter.FilterPropertyChangedEventHandler(this.OnVerticalParallaxChanged);
                    this.UpdateSubtitles();
                    this.FilterWrapper.SourceFormat = TSTFConfig.LAYOUT_MONOSCOPIC;
                    this.FilterWrapper.OutputFormat = CONSTS.METHOD_SOURCE;
                    this.FilterWrapper.Deinterlace = true;

                    if (this.FilterWrapper.SupportsDxva)
                    {
                        this.FilterWrapper.Dxva = true;
                    }

                    this.LoadProperties();
                    this.DetectLetterbox();
                    this.Calibrate();

                    return true;
                }

                LogError("Init", "MP3D could not be initialized because g_Player is null");
                return false;
            }

            LogError("Init", "MP3D could not be initialized because the skin is invalid");
            return false;
        }

        /// <summary>
        /// The load properties.
        /// </summary>
        public void LoadProperties()
        {
            this.Settings.CurrentProfile = this.GetCurrentProfile();

            if (this.Settings.CurrentProfile == 0)
            {
                return;
            }

            this.ApplyProfile(this.Settings.Profiles[this.Settings.CurrentProfile]);
        }

        /// <summary>
        /// The on key down.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        public void OnKeyDown(object sender, ref KeyEventArgs e)
        {
            if (!this.contextMenuShown)
            {
                if (this.FilterWrapper != null && this.FilterWrapper.Processing)
                {
                    return;
                }

                if (this.IsCommandsHotkeyDown(this.monitorCommands, e))
                {
                    e.Handled = true;
                    return;
                }

                if (this.IsMode3D)
                {
                    if (this.IsCommandsHotkeyDown(this.monitorCommandsConditionally, e))
                    {
                        e.Handled = true;
                        return;
                    }

                    if (this.IsCommandsHotkeyDown(this.monitorCommandsFromMenu, e))
                    {
                        e.Handled = true;
                        return;
                    }

                    if (this.IsCommandsHotkeyDown(this.GetMenuCommands(this.CurrentOsd), e))
                    {
                        e.Handled = true;
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// The on key up.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        public void OnKeyUp(object sender, ref KeyEventArgs e)
        {
            if (this.contextMenuShown)
            {
                return;
            }

            if (this.FilterWrapper != null && this.FilterWrapper.Processing)
            {
                return;
            }

            MenuCommand foundCommand;

            if (this.IsCommandsHotkeyUp(this.monitorCommands, e, out foundCommand))
            {
                GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, Object = foundCommand, SendToTargetWindow = true, TargetWindowId = this.GetID });
                e.Handled = true;
                return;
            }

            if (!this.IsMode3D)
            {
                return;
            }

            if (this.IsCommandsHotkeyUp(this.monitorCommandsConditionally, e, out foundCommand))
            {
                GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, Object = foundCommand, SendToTargetWindow = true, TargetWindowId = this.GetID });
                e.Handled = true;
                return;
            }

            if (this.IsCommandsHotkeyUp(this.monitorCommandsFromMenu, e, out foundCommand))
            {
                GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, Object = foundCommand, SendToTargetWindow = true, TargetWindowId = this.GetID });
                e.Handled = true;
                return;
            }

            if (this.IsCommandsHotkeyUp(this.GetMenuCommands(this.CurrentOsd), e, out foundCommand))
            {
                GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, Object = foundCommand, SendToTargetWindow = true, TargetWindowId = this.GetID });
                e.Handled = true;
                return;
            }
        }

        /// <summary>
        /// The refresh.
        /// </summary>
        public void Refresh(bool lightRefresh)
        {
            this.SetupGuiControls();
            this.UpdateGuiProperties();

            if (!lightRefresh)
            {
                this.UpdateSubtitles();
                this.DetectLetterbox();
                this.Calibrate();
            }
        }

        /// <summary>
        /// The save properties.
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        public void SaveProperties()
        {
            foreach (KeyValuePair<int, FilterProperty> pair in this.Settings.FilterProperties)
            {
                this.SaveProperty(pair.Value);
            }
        }

        /// <summary>
        /// The setup gui controls.
        /// </summary>
        public void SetupGuiControls()
        {
            foreach (GUIControl ctrl in this.controlList)
            {
                if (ctrl != null)
                {
                    GUIControl refCtrl = ctrl; // Stupid local ref compiler restriction
                    ctrl.AllocResources();
                    GUIWindowManager.GetWindow(this.ActiveWindowId).Add(ref refCtrl);
                    ctrl.WindowId = this.ActiveWindowId;
                    ctrl.ParentID = this.ActiveWindowId;
                }
            }

            this.sourceFormatProperty = (FilterPropertyEnum)this.Settings.FilterProperties[1];
            this.outputFormatProperty = (FilterPropertyEnum)this.Settings.FilterProperties[2];

            this.monitorCommands = new Collection<MenuCommand>();
            this.monitorCommandsConditionally = new Collection<MenuCommand>();
            this.monitorCommandsFromMenu = new Collection<MenuCommand>();

            foreach (int i in this.Settings.GlobalCommands)
            {
                MenuCommand cmd = this.Settings.MenuCommands[i];

                if (cmd.HotKeyMode3DDependent)
                {
                    this.monitorCommandsConditionally.Add(cmd);
                }
                else
                {
                    this.monitorCommands.Add(cmd);
                }
            }
        }

        /// <summary>
        /// The show menu.
        /// </summary>
        /// <param name="menu">
        /// The menu.
        /// </param>
        /// <returns>
        /// The show menu.
        /// </returns>
        public bool ShowMenu(Menu menu)
        {
            if (this.contextMenuShown)
            {
                return false;
            }

            int backwardSteps = -1;
            return this.ShowMenu(menu, ref backwardSteps);
        }

        /// <summary>
        /// The update gui properties.
        /// </summary>
        public void UpdateGuiProperties()
        {
            GUIPropertyManager.SetProperty(TagIsMode3D, this.IsMode3D.ToString());
            GUIPropertyManager.SetProperty(TagCurrentOsd, this.IsMode3D ? this.CurrentOsd.ToString() : "0");
            GUIPropertyManager.SetProperty(TagMode3DTitleLabel, Translations.GetByName("Mode3DTitleLabel"));
            GUIPropertyManager.SetProperty(TagCurrentSourceFormatLabel, Translations.GetByName("CurrentSourceFormatLabel"));
            GUIPropertyManager.SetProperty(TagCurrentOutputFormatLabel, Translations.GetByName("CurrentOutputFormatLabel"));
            GUIPropertyManager.SetProperty(TagCycleSourceFormatsLabel, Translations.GetByName("CycleSourceFormatsLabel"));
            GUIPropertyManager.SetProperty(TagCycleOutputFormatsLabel, Translations.GetByName("CycleOutputFormatsLabel"));
            GUIPropertyManager.SetProperty(TagHorizontalParallaxLabel, Translations.GetByName("HorizontalParallaxLabel"));
            GUIPropertyManager.SetProperty(TagVerticalParallaxLabel, Translations.GetByName("VerticalParallaxLabel"));

            if (this.IsMode3D && this.FilterWrapper != null)
            {
                GUIPropertyManager.SetProperty(TagCurrentHorizontalParallax, this.FilterWrapper.HorizontalParallax.ToString());
                GUIPropertyManager.SetProperty(TagCurrentVerticalParallax, this.FilterWrapper.VerticalParallax.ToString());

                GUIPropertyManager.SetProperty(TagCurrentSourceFormat, Translations.GetByName(this.Settings.FilterPropertyValues[this.sourceFormatProperty.StereoscopicValueToFilterPropertyValueIds[this.FilterWrapper.SourceFormat]].Name));
                GUIPropertyManager.SetProperty(TagCurrentOutputFormat, Translations.GetByName(this.Settings.FilterPropertyValues[this.outputFormatProperty.StereoscopicValueToFilterPropertyValueIds[this.FilterWrapper.OutputFormat]].Name));
            }
        }

        /// <summary>
        /// The update subtitles.
        /// </summary>
        public void UpdateSubtitles()
        {
            if (!this.Settings.UpdateSubtitles)
            {
                if (SubEngine.engine is StereoSubEngine)
                {
                    SubEngine.engine = ((StereoSubEngine)SubEngine.engine).BaseEngine;
                }
            }
            else
            {
                if (SubEngine.GetInstance() is MpcEngine)
                {
                    SubEngine.engine = new StereoMpcSubEngine(SubEngine.GetInstance());
                }
                else if (SubEngine.GetInstance() is FFDShowEngine)
                {
                    SubEngine.engine = new StereoFfdShowSubEngine(SubEngine.GetInstance());
                }

                if (this.FilterWrapper != null)
                {
                    this.OnOutputFormatChanged(this, new FilterPropertyChangedEventArgs { NewValue = this.FilterWrapper.OutputFormat });
                    this.OnHorizontalParallaxChanged(this, new FilterPropertyChangedEventArgs { NewValue = this.FilterWrapper.HorizontalParallax });
                    this.OnVerticalParallaxChanged(this, new FilterPropertyChangedEventArgs { NewValue = this.FilterWrapper.VerticalParallax });
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The is hot key.
        /// </summary>
        /// <param name="currentKeys">
        /// The current keys.
        /// </param>
        /// <param name="currentModifiers">
        /// The current modifiers.
        /// </param>
        /// <param name="hotkey">
        /// The hotkey.
        /// </param>
        /// <param name="hotkeyModifier1">
        /// The hotkey modifier 1.
        /// </param>
        /// <param name="hotkeyModifier2">
        /// The hotkey modifier 2.
        /// </param>
        /// <param name="hotkeyModifier3">
        /// The hotkey modifier 3.
        /// </param>
        /// <returns>
        /// The is hot key.
        /// </returns>
        private static bool IsHotKey(Keys currentKeys, Keys currentModifiers, Keys hotkey, Keys hotkeyModifier1, Keys hotkeyModifier2, Keys hotkeyModifier3)
        {
            if (currentKeys == Keys.None)
            {
                return false;
            }

            if (hotkey != currentKeys)
            {
                return false;
            }

            if (hotkeyModifier1 == Keys.None)
            {
                return true;
            }

            if (currentModifiers != hotkeyModifier1)
            {
                return false;
            }

            if (hotkeyModifier2 == Keys.None)
            {
                return true;
            }

            if (currentModifiers != hotkeyModifier2)
            {
                return false;
            }

            if (hotkeyModifier3 == Keys.None)
            {
                return true;
            }

            return currentModifiers == hotkeyModifier3;
        }

        /// <summary>
        /// The apply profile.
        /// </summary>
        /// <param name="profile">
        /// The profile.
        /// </param>
        private void ApplyProfile(Profile profile)
        {
            if (profile == null)
            {
                LogError("ApplyProfile", "Could not apply null profile");
                return;
            }

            if (!profile.Enabled)
            {
                LogError("ApplyProfile", "Could not apply disabled profile");
                return;
            }

            foreach (var pair in profile.ProfileProperties)
            {
                ProfileProperty property = pair.Value;
                int value = property.DefaultValue;

                switch (property.PropertyType)
                {
                    case ProfileProperty.ProfilePropertyType.SavePerMediaType:
                        ProfilePropertyPerMediaType perMedia = (ProfilePropertyPerMediaType)property;

                        if (perMedia.Value.ContainsKey(this.CurrentMediaType))
                        {
                            value = perMedia.Value[this.CurrentMediaType];
                        }

                        break;
                    case ProfileProperty.ProfilePropertyType.SavePerSource:
                        ProfilePropertyPerSource perSource = (ProfilePropertyPerSource)property;
                        string hash = this.GetCurrentMediaHash();

                        if (perSource.Value.ContainsKey(hash))
                        {
                            value = perSource.Value[hash];
                        }

                        break;
                }

                int stereoProperty = this.Settings.FilterProperties[property.PropertyId].StereoscopicFilterPropertyId;
                this.FilterWrapper.SetProperty(stereoProperty, value);
            }

            this.UpdateGuiProperties();
        }

        /// <summary>
        /// The calibrate.
        /// </summary>
        private void Calibrate()
        {
            if (this.FilterWrapper.Initialized)
            {
                if (this.calibrator != null)
                {
                    this.calibrator.Dispose();
                }

                if (this.Settings.StereoscopicMenu)
                {
                    this.calibrator = new Calibrator { OutputFormat = this.FilterWrapper.OutputFormat };
                    this.calibrator.Start();
                }
            }
        }

        /// <summary>
        /// The execute command.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <param name="backwardSteps">
        /// The backward steps.
        /// </param>
        /// <returns>
        /// The execute command.
        /// </returns>
        private bool ExecuteCommand(MenuCommand command, ref int backwardSteps)
        {
            switch (command.CommandType)
            {
                case MenuCommand.MenuCommandType.Nothing:
                    backwardSteps = -1;
                    return true;
                case MenuCommand.MenuCommandType.PropertySet:
                    backwardSteps = -1;
                    return this.ExecuteCommandPropertySet(command);
                case MenuCommand.MenuCommandType.PropertyIncrease:
                    backwardSteps = -1;
                    return this.ExecuteCommandPropertyCycle(command, 1);
                case MenuCommand.MenuCommandType.PropertyDecrease:
                    backwardSteps = -1;
                    return this.ExecuteCommandPropertyCycle(command, -1);
                case MenuCommand.MenuCommandType.GotoMenu:
                    return this.ExecuteCommandGoTo(command, ref backwardSteps);
                case MenuCommand.MenuCommandType.PrevMenu:
                    backwardSteps = 1;
                    return true;
                case MenuCommand.MenuCommandType.UpdateProfile:
                    this.Settings.UpdateProfile = !this.Settings.UpdateProfile;
                    this.SaveProperties();
                    backwardSteps = -1;
                    return true;
                case MenuCommand.MenuCommandType.ApplyProfile:
                    backwardSteps = -1;
                    return this.ExecuteCommandApplyProfile(command);
                case MenuCommand.MenuCommandType.SetMode3D:
                    if (!this.ParentPlugin.Mode3DIsEnabled)
                    {
                        this.ParentPlugin.Enable3DProcessing();
                        return false;
                    }

                    if (this.longPress)
                    {
                        this.ParentPlugin.Disable3DProcessing();
                        return false;
                    }

                    this.IsMode3D = !this.IsMode3D;
                    this.UpdateGuiProperties();
                    this.ShowMenu(this.Settings.Menus[this.CurrentOsd]);
                    backwardSteps = -1;
                    return true;
                case MenuCommand.MenuCommandType.UpdateSubtitles:
                    this.Settings.UpdateSubtitles = !this.Settings.UpdateSubtitles;
                    this.UpdateSubtitles();
                    return false; // Do not reshow the menu
                case MenuCommand.MenuCommandType.DetectLetterbox:
                    this.Settings.DetectLetterbox = !this.Settings.DetectLetterbox;
                    this.DetectLetterbox();
                    return false; // Do not reshow the menu
                case MenuCommand.MenuCommandType.StereoscopicMenu:
                    this.Settings.StereoscopicMenu = !this.Settings.StereoscopicMenu;
                    this.Calibrate();
                    return false; // Do not reshow the menu
                default:
                    LogError("ExecuteCommand", "CommandType out of range");
                    return false;
            }
        }

        /// <summary>
        /// The execute command apply profile.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The execute command apply profile.
        /// </returns>
        private bool ExecuteCommandApplyProfile(MenuCommand command)
        {
            MenuCommandApplyProfile cmd = command as MenuCommandApplyProfile;

            if (cmd == null)
            {
                LogError("ExecuteCommandApplyProfile", "Could not cast to MenuCommandApplyProfile");
                return false;
            }

            if (!this.Settings.Profiles.ContainsKey(cmd.ProfileId))
            {
                LogError("ExecuteCommandApplyProfile", "Could not find profile id " + cmd.ProfileId);
                return false;
            }

            this.ApplyProfile(this.Settings.Profiles[cmd.ProfileId]);
            return true;
        }

        /// <summary>
        /// The execute command go to.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <param name="backwardSteps">
        /// The backward steps.
        /// </param>
        /// <returns>
        /// The execute command go to.
        /// </returns>
        private bool ExecuteCommandGoTo(MenuCommand command, ref int backwardSteps)
        {
            if (!(command is MenuCommandGoTo))
            {
                LogError("ExecuteCommandGoTo", "MenuCommand is not of type MenuCommandGoto");
                return false;
            }

            if (this.Settings == null)
            {
                LogError("ExecuteCommandGoTo", "Settings are not initialized");
                return false;
            }

            MenuCommandGoTo commandGoTo = (MenuCommandGoTo)command;

            if (!this.Settings.Menus.ContainsKey(commandGoTo.MenuId))
            {
                LogWarn("ExecuteCommandGoTo", "MenuId {0} not found", commandGoTo.MenuId);
                return true;
            }

            Menu menu = this.Settings.Menus[commandGoTo.MenuId];
            return this.ShowMenu(menu, ref backwardSteps);
        }

        /// <summary>
        /// The execute command property cycle.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <param name="directionSign">
        /// The direction sign.
        /// </param>
        /// <returns>
        /// The execute command property cycle.
        /// </returns>
        private bool ExecuteCommandPropertyCycle(MenuCommand command, int directionSign)
        {
            if (!(command is MenuCommandPropertyCycle))
            {
                LogError("ExecuteCommandPropertyCycle", "MenuCommand is not of type MenuCommandPropertyCycle");
                return false;
            }

            if (this.Settings == null)
            {
                LogError("ExecuteCommandPropertyCycle", "Settings are not initialized");
                return false;
            }

            MenuCommandPropertyCycle commandPropertyCycle = (MenuCommandPropertyCycle)command;

            if (!this.Settings.FilterProperties.ContainsKey(commandPropertyCycle.PropertyId))
            {
                LogWarn("ExecuteCommandPropertyCycle", "PropertyId {0} not found", commandPropertyCycle.PropertyId);
                return true;
            }

            FilterProperty property = this.Settings.FilterProperties[commandPropertyCycle.PropertyId];
            int value = this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId);

            switch (property.PropertyType)
            {
                case FilterProperty.FilterPropertyTypes.Enumeration:
                    if (!(property is FilterPropertyEnum))
                    {
                        LogWarn("ExecuteCommandPropertyCycle", "Property Id {0} is not of type FilterPropertyEnum", property.Id);
                        return true;
                    }

                    FilterPropertyEnum propertyEnum = (FilterPropertyEnum)property;

                    if (propertyEnum.StereoscopicValueToFilterPropertyValueIds.ContainsKey(value))
                    {
                        int valueEnumId = propertyEnum.StereoscopicValueToFilterPropertyValueIds[value];

                        if (!(this.Settings.FilterPropertyValues[valueEnumId] is FilterPropertyValueEnum))
                        {
                            LogWarn("ExecuteCommandPropertyCycle", "PropertyId {0} is not of type FilterPropertyValueEnum", valueEnumId);
                            return true;
                        }

                        FilterPropertyValueEnum valueEnum = (FilterPropertyValueEnum)this.Settings.FilterPropertyValues[valueEnumId];

                        int cyclePropertyId = valueEnum.NextPropertyValueId;
                        FilterPropertyValue cycleProperty = null;
                        int i = 0;

                        while (cyclePropertyId != valueEnum.Id && i++ < 100)
                        {
                            if (directionSign == -1)
                            {
                                cyclePropertyId = valueEnum.PrevPropertyValueId;
                            }

                            if (!this.Settings.FilterPropertyValues.ContainsKey(cyclePropertyId))
                            {
                                LogWarn("ExecuteCommandPropertyCycle", "PropertyId {0} not found to cycle", cyclePropertyId);
                                return true;
                            }

                            cycleProperty = this.Settings.FilterPropertyValues[cyclePropertyId];

                            if (cycleProperty.Enabled)
                            {
                                break;
                            }

                            if (!(cycleProperty is FilterPropertyValueEnum))
                            {
                                LogWarn("ExecuteCommandPropertyCycle", "Disabled propertyId {0} is not Enum to get the next enabled", cyclePropertyId);
                                return true;
                            }

                            valueEnum = (FilterPropertyValueEnum)cycleProperty;
                            cyclePropertyId = valueEnum.NextPropertyValueId;
                        }

                        if (cycleProperty == null)
                        {
                            LogWarn("ExecuteCommandPropertyCycle", "PropertyId {0} does not have a valid cycle", valueEnum.Id);
                            return true;
                        }

                        this.FilterWrapper.SetProperty(property.StereoscopicFilterPropertyId, cycleProperty.Value);
                        this.UpdateGuiProperties();
                    }

                    this.SaveProperties();
                    return true;
                case FilterProperty.FilterPropertyTypes.Integer:
                    this.FixCycleProperty(value, directionSign, property.StereoscopicFilterPropertyId, commandPropertyCycle.Step);
                    this.UpdateGuiProperties();
                    this.SaveProperties();
                    return true;
                case FilterProperty.FilterPropertyTypes.Boolean:
                    this.FilterWrapper.SetProperty(property.StereoscopicFilterPropertyId, value == 0 ? 0 : 1);
                    this.UpdateGuiProperties();
                    this.SaveProperties();
                    return true;
                default:
                    LogError("ExecuteCommandPropertyCycle", "PropertyType out of range");
                    return false;
            }
        }

        /// <summary>
        /// The execute command property set.
        /// </summary>
        /// <param name="command">
        /// The command.
        /// </param>
        /// <returns>
        /// The execute command property set.
        /// </returns>
        private bool ExecuteCommandPropertySet(MenuCommand command)
        {
            if (!(command is MenuCommandPropertySet))
            {
                LogError("ExecuteCommandPropertySet", "MenuCommand is not of type MenuCommandPropertySet");
                return false;
            }

            if (this.Settings == null)
            {
                LogError("ExecuteCommandPropertySet", "Settings are not initialized");
                return false;
            }

            MenuCommandPropertySet commandPropertySet = (MenuCommandPropertySet)command;

            if (!this.Settings.FilterProperties.ContainsKey(commandPropertySet.PropertyId))
            {
                LogWarn("ExecuteCommandPropertySet", "PropertyId {0} not found", commandPropertySet.PropertyId);
                return true;
            }

            FilterProperty property = this.Settings.FilterProperties[commandPropertySet.PropertyId];

            switch (property.PropertyType)
            {
                case FilterProperty.FilterPropertyTypes.Enumeration:
                    if (!this.Settings.FilterPropertyValues.ContainsKey(commandPropertySet.Value))
                    {
                        LogWarn("ExecuteCommandPropertySet", "PropertyValueId {0} not found", commandPropertySet.Value);
                        return true;
                    }

                    FilterPropertyValue value = this.Settings.FilterPropertyValues[commandPropertySet.Value];

                    this.FilterWrapper.SetProperty(property.StereoscopicFilterPropertyId, value.Value);
                    this.UpdateGuiProperties();
                    break;
                case FilterProperty.FilterPropertyTypes.Integer:
                    this.FilterWrapper.SetProperty(property.StereoscopicFilterPropertyId, commandPropertySet.Value);
                    this.UpdateGuiProperties();
                    break;
                case FilterProperty.FilterPropertyTypes.Boolean:
                    this.FilterWrapper.SetProperty(property.StereoscopicFilterPropertyId, commandPropertySet.Value == 0 ? 0 : 1);
                    this.UpdateGuiProperties();
                    break;
                default:
                    LogError("ExecuteCommandPropertySet", "PropertyType out of range");
                    return false;
            }

            this.SaveProperty(property);
            return true;
        }

        /// <summary>
        /// The fix cycle property.
        /// </summary>
        /// <param name="currentValue">
        /// The current value.
        /// </param>
        /// <param name="directionSign">
        /// The direction sign.
        /// </param>
        /// <param name="stereoscopicFilterPropertyId">
        /// The stereoscopic filter property id.
        /// </param>
        /// <param name="step">
        /// The step.
        /// </param>
        private void FixCycleProperty(int currentValue, int directionSign, int stereoscopicFilterPropertyId, MenuCommandPropertyCycle.CycleStep step)
        {
            int prevValue = currentValue;
            int i = 0;
            int stepValue = 1;

            switch (step)
            {
                case MenuCommandPropertyCycle.CycleStep.ParallaxStep:
                    stepValue = this.Settings.ParallaxStep;
                    break;
            }

            this.FilterWrapper.SetProperty(stereoscopicFilterPropertyId, prevValue + (stepValue * directionSign));
            int curValue = this.FilterWrapper.GetProperty(stereoscopicFilterPropertyId);

            while (curValue == prevValue)
            {
                i += stepValue * directionSign;
                prevValue = curValue;
                this.FilterWrapper.SetProperty(stereoscopicFilterPropertyId, prevValue + i);
                curValue = this.FilterWrapper.GetProperty(stereoscopicFilterPropertyId);
            }
        }

        /// <summary>
        /// The get current media hash.
        /// </summary>
        /// <returns>
        /// The get current media hash result.
        /// </returns>
        private string GetCurrentMediaHash()
        {
            if (!string.IsNullOrEmpty(this.CurrentFileHash))
            {
                return this.CurrentFileHash;
            }

            this.CurrentFileHash = !string.IsNullOrEmpty(GUIPropertyManager.GetProperty("#TV.View.channel")) ? GUIPropertyManager.GetProperty("#TV.View.channel") : g_Player.CurrentFile;
            return this.CurrentFileHash;
        }

        /// <summary>
        /// The get current profile.
        /// </summary>
        /// <returns>
        /// The get current profile result.
        /// </returns>
        private int GetCurrentProfile()
        {
            if (this.Settings.Profiles == null || this.Settings.Profiles.Count == 0)
            {
                LogError("SaveProperty", "No profiles found!");
                return 0;
            }

            if (this.Settings.CurrentProfile == 0)
            {
                LogWarn("SaveProperty", "No default profile found, using the first one");

                foreach (var pair in this.Settings.Profiles)
                {
                    if (!pair.Value.Enabled)
                    {
                        continue;
                    }

                    return pair.Key;
                }

                LogError("SaveProperty", "No enabled profiles found!!");
                return 0;
            }

            return this.Settings.CurrentProfile;
        }

        /// <summary>
        /// The get menu commands.
        /// </summary>
        /// <param name="menuId">
        /// The menu id.
        /// </param>
        /// <returns>
        /// The menu commands.
        /// </returns>
        private IEnumerable<MenuCommand> GetMenuCommands(int menuId)
        {
            Collection<MenuCommand> commands = new Collection<MenuCommand>();

            if (this.Settings.Menus.Count < menuId || menuId <= 0)
            {
                return commands;
            }

            foreach (int menuCommandId in this.Settings.Menus[menuId].MenuCommandIds)
            {
                commands.Add(this.Settings.MenuCommands[menuCommandId]);
            }

            return commands;
        }

        /// <summary>
        /// The is commands hotkey down.
        /// </summary>
        /// <param name="commands">
        /// The commands.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        /// <returns>
        /// The is commands hotkey down result.
        /// </returns>
        private bool IsCommandsHotkeyDown(IEnumerable<MenuCommand> commands, KeyEventArgs e)
        {
            foreach (MenuCommand command in commands)
            {
                if (IsHotKey(e.KeyCode, e.Modifiers, command.HotKey, command.HotKeyModifier1, command.HotKeyModifier2, command.HotKeyModifier3))
                {
                    if (e.KeyCode != this.hotKeyDown || e.Modifiers != this.hotKeyDownModifiers)
                    {
                        this.hotKeyDown = e.KeyCode;
                        this.hotKeyDownModifiers = e.Modifiers;
                        this.hotKeyDownTick = DateTime.Now;
                    }

                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// The is commands hotkey up.
        /// </summary>
        /// <param name="commands">
        /// The commands.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        /// <param name="foundCommand">
        /// The found command.
        /// </param>
        /// <returns>
        /// The is commands hotkey up.
        /// </returns>
        private bool IsCommandsHotkeyUp(IEnumerable<MenuCommand> commands, KeyEventArgs e, out MenuCommand foundCommand)
        {
            foundCommand = null;

            foreach (MenuCommand command in commands)
            {
                if (IsHotKey(e.KeyCode, e.Modifiers, command.HotKey, command.HotKeyModifier1, command.HotKeyModifier2, command.HotKeyModifier3))
                {
                    if (this.hotKeyDown == e.KeyCode && this.hotKeyDownModifiers == e.Modifiers && (DateTime.Now - this.hotKeyDownTick).TotalMilliseconds >= this.Settings.LongPressMilliseconds)
                    {
                        this.longPress = true;
                    }
                    else
                    {
                        this.longPress = false;
                    }

                    this.hotKeyDown = Keys.None;
                    this.hotKeyDownModifiers = Keys.None;
                    foundCommand = command;
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// The on horizontal parallax changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnHorizontalParallaxChanged(object sender, FilterPropertyChangedEventArgs e)
        {
            if (this.FilterWrapper.Filter == null)
            {
                return;
            }

            if (SubEngine.engine is StereoSubEngine)
            {
                ((StereoSubEngine)SubEngine.engine).HorizontalParallax = e.NewValue;
            }
        }

        /// <summary>
        /// The on output format changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnOutputFormatChanged(object sender, FilterPropertyChangedEventArgs e)
        {
            if (this.FilterWrapper.Filter == null)
            {
                return;
            }

            if (SubEngine.engine is StereoSubEngine && this.FilterWrapper.Filter is ISTFConfig)
            {
                int x;
                int y;
                ((ISTFConfig)this.FilterWrapper.Filter).GetOutputSize(out x, out y);
                ((StereoSubEngine)SubEngine.engine).OutputFormat = e.NewValue;
                ((StereoSubEngine)SubEngine.engine).VideoWidth = x;
                ((StereoSubEngine)SubEngine.engine).VideoHeight = y;
            }

            if (this.calibrator != null)
            {
                this.calibrator.OutputFormat = e.NewValue;
            }
        }

        /// <summary>
        /// The on vertical parallax changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnVerticalParallaxChanged(object sender, FilterPropertyChangedEventArgs e)
        {
            if (this.FilterWrapper.Filter == null)
            {
                return;
            }

            if (SubEngine.engine is StereoSubEngine)
            {
                ((StereoSubEngine)SubEngine.engine).VerticalParallax = e.NewValue;
            }
        }

        /// <summary>
        /// The on view mode switcher crop.
        /// </summary>
        /// <param name="left">
        /// The left.
        /// </param>
        /// <param name="top">
        /// The top.
        /// </param>
        /// <param name="right">
        /// The right.
        /// </param>
        /// <param name="bottom">
        /// The bottom.
        /// </param>
        private void OnViewModeSwitcherCrop(int left, int top, int right, int bottom)
        {
            this.FilterWrapper.SetProperty(TSTFConfig.PROP_CROPLEFT, left);
            this.FilterWrapper.SetProperty(TSTFConfig.PROP_CROPTOP, top);
            this.FilterWrapper.SetProperty(TSTFConfig.PROP_CROPRIGHT, right);
            this.FilterWrapper.SetProperty(TSTFConfig.PROP_CROPBOTTOM, bottom);
        }

        /// <summary>
        /// The save property.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        private void SaveProperty(FilterProperty property)
        {
            if (!this.Settings.UpdateProfile)
            {
                return;
            }

            this.Settings.CurrentProfile = this.GetCurrentProfile();

            if (this.Settings.CurrentProfile == 0)
            {
                return;
            }

            Profile currentProfile = this.Settings.Profiles[this.Settings.CurrentProfile];
            ProfileProperty profileProperty;

            if (!currentProfile.ProfileProperties.TryGetValue(property.Id, out profileProperty))
            {
                return;
            }

            switch (profileProperty.PropertyType)
            {
                case ProfileProperty.ProfilePropertyType.SaveGlobally:
                    profileProperty.DefaultValue = this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId);
                    break;
                case ProfileProperty.ProfilePropertyType.SavePerMediaType:
                    if (this.CurrentMediaType == g_Player.MediaType.Unknown)
                    {
                        LogError("SaveProperty", "Current media type not supported");
                        return;
                    }

                    ProfilePropertyPerMediaType perMedia = (ProfilePropertyPerMediaType)profileProperty;

                    if (perMedia.Value.ContainsKey(this.CurrentMediaType))
                    {
                        perMedia.Value[this.CurrentMediaType] = this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId);
                    }
                    else
                    {
                        perMedia.Value.Add(this.CurrentMediaType, this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId));
                    }

                    break;
                case ProfileProperty.ProfilePropertyType.SavePerSource:
                    string hash = this.GetCurrentMediaHash();

                    ProfilePropertyPerSource perSource = (ProfilePropertyPerSource)profileProperty;

                    if (perSource.Value.ContainsKey(hash))
                    {
                        perSource.Value[hash] = this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId);
                    }
                    else
                    {
                        perSource.Value.Add(hash, this.FilterWrapper.GetProperty(property.StereoscopicFilterPropertyId));
                    }

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// The show dialog menu.
        /// </summary>
        /// <param name="menu">
        /// The menu.
        /// </param>
        /// <param name="backwardSteps">
        /// The backward steps.
        /// </param>
        /// <returns>
        /// The show dialog menu.
        /// </returns>
        private bool ShowDialogMenu(Menu menu, ref int backwardSteps)
        {
            if (this.dlg == null)
            {
                this.dlg = (GUIDialogMenu)GUIWindowManager.GetWindow((int)Window.WINDOW_DIALOG_MENU);
            }

            if (this.dlg == null)
            {
                LogError("ShowDialogMenu", "Dialog menu could not be initialized");
                return false;
            }

            this.dlg.Reset();
            this.dlg.SetHeading(Translations.GetByName(menu.Name));
            Dictionary<int, MenuCommand> commands = new Dictionary<int, MenuCommand>();
            int initialOptionCommandId = -1;

            if (menu.InitialOptionType == Menu.MenuInitialOptionTypes.FromFilterProperty && menu.InitialOptionPropertyId > -1)
            {
                int stereoProperty = this.Settings.FilterProperties[menu.InitialOptionPropertyId].StereoscopicFilterPropertyId;
                int initialOptionValue = this.FilterWrapper.GetProperty(stereoProperty);
                if (menu.InitialOptionValues.ContainsKey(initialOptionValue))
                {
                    initialOptionCommandId = menu.InitialOptionValues[initialOptionValue];
                }
            }

            if (menu.MenuType == Menu.MenuTypes.DialogWithProfiles && this.Settings.Profiles.Count > 1)
            {
                int i = -1;

                foreach (var pair in this.Settings.Profiles)
                {
                    Profile profile = pair.Value;
                    string name = string.Concat(Translations.GetByName("Profile"), ": ", profile.Name);
                    string iconImage;

                    if (this.Settings.CurrentProfile == profile.Id)
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_enabled.png";
                    }
                    else
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_disabled.png";
                    }

                    GUIListItem li = new GUIListItem(name) { IconImage = iconImage };
                    this.dlg.Add(li);
                    MenuCommandApplyProfile cmd = new MenuCommandApplyProfile { Id = i--, CommandType = MenuCommand.MenuCommandType.ApplyProfile, Enabled = true, ProfileId = profile.Id };
                    commands.Add(commands.Count + 1, cmd);
                }
            }

            foreach (int commandId in menu.MenuCommandIds)
            {
                if (this.Settings == null)
                {
                    LogError("ShowDialogMenu", "Configuration is not initialized");
                    return false;
                }

                if (!this.Settings.MenuCommands.ContainsKey(commandId))
                {
                    LogWarn("ShowDialogMenu", "CommandId {0} not found. Ignoring", commandId);
                    continue;
                }

                MenuCommand command = this.Settings.MenuCommands[commandId];
                string name = Translations.GetByName(command.Name);
                string iconImage = string.Concat(Helper.Helper.PluginName, @"\", command.Name, ".png");

                if (!File.Exists(Path.Combine(Config.GetFolder(MediaPortal.Configuration.Config.Dir.Skin), string.Concat(@"Media\", iconImage))))
                {
                    iconImage = string.Empty;
                }
                
                if (command.CommandType == MenuCommand.MenuCommandType.GotoMenu || command.CommandType == MenuCommand.MenuCommandType.PrevMenu)
                {
                    name = string.Concat(name, " ...");
                }
                else if (command.CommandType == MenuCommand.MenuCommandType.UpdateProfile)
                {
                    if (this.Settings.UpdateProfile)
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_enabled.png";
                    }
                    else
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_disabled.png";
                    }
                }
                else if (command.CommandType == MenuCommand.MenuCommandType.UpdateSubtitles)
                {
                    if (this.Settings.UpdateSubtitles)
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_enabled.png";
                    }
                    else
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_disabled.png";
                    }
                }
                else if (command.CommandType == MenuCommand.MenuCommandType.DetectLetterbox)
                {
                    if (this.Settings.DetectLetterbox)
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_enabled.png";
                    }
                    else
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_disabled.png";
                    }
                }
                else if (command.CommandType == MenuCommand.MenuCommandType.StereoscopicMenu)
                {
                    if (this.Settings.StereoscopicMenu)
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_enabled.png";
                    }
                    else
                    {
                        iconImage = Helper.Helper.PluginName + @"\option_disabled.png";
                    }
                }

                GUIListItem li = new GUIListItem(name) { IconImage = iconImage };
                this.dlg.Add(li);
                commands.Add(commands.Count + 1, command);

                if (command.Id == initialOptionCommandId)
                {
                    menu.InitialOption = commands.Count - 1;
                }
            }

            this.dlg.SelectedLabel = menu.InitialOption;

            this.contextMenuShown = true;
            this.dlg.DoModal(this.ActiveWindowId);
            this.contextMenuShown = false;

            if (this.dlg.SelectedId == -1)
            {
                backwardSteps = -1;
                return true;
            }

            if (!commands.ContainsKey(this.dlg.SelectedId))
            {
                LogWarn("ShowDialogMenu", "The command selected was not found in commands added to dialog");
                return true;
            }

            if (menu.InitialOptionType == Menu.MenuInitialOptionTypes.RememberLast)
            {
                menu.InitialOption = this.dlg.SelectedLabel;
            }

            return this.ExecuteCommand(commands[this.dlg.SelectedId], ref backwardSteps);
        }

        /// <summary>
        /// The show menu.
        /// </summary>
        /// <param name="menu">
        /// The menu.
        /// </param>
        /// <param name="backwardSteps">
        /// The backward steps.
        /// </param>
        /// <returns>
        /// The show menu.
        /// </returns>
        private bool ShowMenu(Menu menu, ref int backwardSteps)
        {
            if (menu == null)
            {
                return false;
            }

            bool loop;

            do
            {
                this.monitorCommandsFromMenu.Clear();

                foreach (int i in menu.MenuCommandIds)
                {
                    this.monitorCommandsFromMenu.Add(this.Settings.MenuCommands[i]);
                }

                switch (menu.MenuType)
                {
                    case Menu.MenuTypes.Osd:
                        this.CurrentOsd = menu.Id;
                        this.UpdateGuiProperties();
                        break;
                    case Menu.MenuTypes.Dialog:
                    case Menu.MenuTypes.DialogWithProfiles:
                        if (!this.ShowDialogMenu(menu, ref backwardSteps))
                        {
                            return false;
                        }

                        break;
                    default:
                        LogError("ExecuteCommand", "MenuType out of range");
                        return false;
                }

                if (backwardSteps == -1)
                {
                    loop = menu.AfterCommandAction == Menu.MenuAfterCommandActions.Visible;
                }
                else if (backwardSteps == 0)
                {
                    loop = true;
                    backwardSteps = -1;
                }
                else
                {
                    loop = false;
                    backwardSteps--;
                }
            }
            while (loop);

            return true;
        }

        #endregion
    }
}